import matplotlib
matplotlib.use("WXAgg")
matplotlib.interactive(True)
from matplotlib.backends.backend_wxagg import Toolbar, FigureCanvasWxAgg
from matplotlib.figure import Figure
from matplotlib.axes import Subplot
 
import wx
import wx.lib.newevent
import threading
import time
import ezdaq
import os

ID_STARTBUTTON=101
ID_STOPBUTTON=102
ID_CLEARBUTTON=103
ID_OKBUTTON=104
ID_CONFIGUREBUTTON=105
X_INIT=-.1
Y_INIT=.1
ARRAY_LIM=100000

# this creates an UpdateCanvasEvent even class that will get created
# and sent from the worker thread to the main thread (which is the
# only place that updates to the GUI should happen...lest there be crashes)
(UpdateCanvasEvent, EVT_UPDATE_CANVAS) = wx.lib.newevent.NewEvent()

class MainWindow(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self,None,-1,"Free Period Calculation")

	self.arrayLock = threading.Semaphore(1)	
	self.canvasLock = threading.Semaphore(1)

        #create the preferences window
	self.prefwindow = self.createPreferencesWindow()

	self.figure = Figure((5,4),75)
	self.canvas = FigureCanvasWxAgg(self, -1, self.figure)
        self.toolbar = Toolbar(self.canvas)
        self.toolbar.Realize()

        self.sizerHoriz = wx.BoxSizer(wx.HORIZONTAL)
	
	self.startButton = wx.Button(self, ID_STARTBUTTON, "Start")
	wx.EVT_BUTTON(self, ID_STARTBUTTON, self.pressStartButton);

	self.stopButton = wx.Button(self, ID_STOPBUTTON, "Stop")
	self.stopButton.Enable(0)
	wx.EVT_BUTTON(self, ID_STOPBUTTON, self.pressStopButton);

	self.clearButton = wx.Button(self, ID_CLEARBUTTON, "Clear")
	wx.EVT_BUTTON(self, ID_CLEARBUTTON, self.pressClearButton);

	configureButton = wx.Button(self, ID_CONFIGUREBUTTON, "Configure")
	wx.EVT_BUTTON(self, ID_CONFIGUREBUTTON, self.pressConfigureButton)

	self.sizerHoriz.Add(self.startButton,0,wx.ALL,5)
	self.sizerHoriz.Add(self.stopButton,0,wx.ALL, 5)
	self.sizerHoriz.Add(self.clearButton,0,wx.ALL, 5)
	self.sizerHoriz.Add(configureButton,0,wx.ALL, 5)
	
        # Use some sizers to see layout options
        self.sizerVert=wx.BoxSizer(wx.VERTICAL)
        self.sizerVert.Add(self.sizerHoriz,0,wx.EXPAND)
	self.sizerVert.Add(self.canvas,1,wx.EXPAND)
        self.sizerVert.Add(self.toolbar,0,wx.GROW)

	#Bind the update canvas event to some function:
	self.Bind(EVT_UPDATE_CANVAS, self.onUpdateCanvas)
	
        #Layout sizers
        self.SetSizer(self.sizerVert)
        self.SetAutoLayout(1)
        self.sizerVert.Fit(self)
	self.initialize()
        self.Show(1)

    def GetToolBar(self):
        return self.toolbar

    def pressStartButton(self, event):
	print "Starting background thread."
	self.startButton.Enable(0)
        self.configuration = self.prefwindow.getConfiguration()
        ezdaq.configure_device(self.configuration)
        ezdaq.configure_streaming(self.configuration)
	self.helper = helperThread(self)
        ezdaq.start_streaming()
	self.helper.start()
	self.stopButton.Enable(1)
	print "Start button complete."

    def pressStopButton(self, event):
	self.stopButton.Enable(0)
	self.helper.stopFlag = 0
	self.helper.join()
        ezdaq.stop_streaming()
        ezdaq.close_device()
	print "Background thread stopped."
	self.helper = None
	self.startButton.Enable(1)

    def pressClearButton(self,event):
	self.arrayLock.acquire()
	self.xarray = [X_INIT,Y_INIT]
	self.yarray = [Y_INIT,X_INIT]
        self.line.set_data(self.xarray,self.yarray)
	self.arrayLock.release()
	evt = UpdateCanvasEvent()
	wx.PostEvent(self, evt)

    def pressConfigureButton(self, event):
	self.prefwindow.Show(1)
	self.prefwindow.MakeModal(1)
	
    def initialize(self):
   	self.subplot = self.figure.add_subplot(111)
	self.xarray = [X_INIT,Y_INIT]
	self.yarray = [Y_INIT,X_INIT]
        self.line, = self.subplot.plot(self.xarray,self.yarray,'.')
        self.toolbar.update()

    def onUpdateCanvas(self, evt):
	print "Updating the canvas..."
	self.canvasLock.acquire()
	self.canvas.draw()
        self.toolbar.update()
	self.canvasLock.release()

    def createPreferencesWindow(self):
	prefwindow = PreferencesFrame(self, "Preferences") 
	prefwindow.CentreOnParent(wx.BOTH)
	return prefwindow

class helperThread(threading.Thread):
    def __init__(self,window):
        threading.Thread.__init__(self)
	self.window = window

    def run(self):
	print "Inside thread run method"
	self.stopFlag = 1

	while(self.stopFlag):
                try:
		    newlists = ezdaq.get_results()
                    if(newlists==None):
                        print "Waiting..."
                        #if exception is thrown, that means the buffer was empty and we have no results
		        #to post to the cavnas. While we're in here and otherwise waiting, lets trim the 
                        #list down if it gets to big
                        array_length=len(self.window.xarray)
                        if(array_length>ARRAY_LIM):
                            del self.window.xarray[0:array_length-ARRAY_LIM]
                            del self.window.yarray[0:array_length-ARRAY_LIM]
                        time.sleep(1) #change this to something smaller
                        continue
                except IOError:
                    print "Fatal Error"
                    break
		print "Got the data."
                newxlist = newlists[0]
                newylist = newlists[1]
                self.window.arrayLock.acquire()
                self.window.xarray.extend(newxlist)
		self.window.yarray.extend(newylist)
		self.window.line.set_data(self.window.xarray,self.window.yarray)
		self.window.arrayLock.release()
		evt = UpdateCanvasEvent()
		wx.PostEvent(self.window, evt)
	

class PreferencesFrame(wx.Frame):
    def __init__(self, parent, title):
        wx.Frame.__init__(self, parent, -1, title)

        #p = wx.Panel(self, -1)
	vertSizer = wx.BoxSizer(wx.VERTICAL)
	titleLabel = wx.StaticText(self,-1, "Comedi Configuration")
	okButton = wx.Button(self, ID_OKBUTTON, "Save Changes")
	wx.EVT_BUTTON(self, ID_OKBUTTON, self.PressOkButton)

	sizer = wx.GridSizer(6,2,2,2) #rows, columns, hgap, vgap

	vertSizer.Add(titleLabel,0,wx.ALIGN_CENTER|wx.ALL,8)
	vertSizer.Add(sizer)
	vertSizer.Add(okButton,0,wx.ALIGN_RIGHT|wx.ALL,15)

	deviceNameLabel = wx.StaticText(self, -1, "Device: ")
	self.deviceNameTextBox = wx.TextCtrl(self, -1, "/dev/comedi0")
	sizer.Add(deviceNameLabel,0,wx.ALIGN_RIGHT)
	sizer.Add(self.deviceNameTextBox)

	xInputLabel = wx.StaticText(self, -1, "X Input Channel: ")
	self.xInputBox = wx.Choice(self,-1, choices=['0','1','2','3','4','5','6','7'])
	self.xInputBox.SetSelection(0)
	sizer.Add(xInputLabel,0,wx.ALIGN_RIGHT)
	sizer.Add(self.xInputBox)

	xInputRangeLabel = wx.StaticText(self, -1, "X Input Range: ")
	self.xInputRangeBox = wx.Choice(self,-1, choices=['0','1','2','3'])
	self.xInputRangeBox.SetSelection(1)
	sizer.Add(xInputRangeLabel,0,wx.ALIGN_RIGHT)
	sizer.Add(self.xInputRangeBox)

	xInputTypeLabel = wx.StaticText(self, -1, "X Input Type: ")
	self.xInputTypeBox = wx.Choice(self,-1, choices=['AREF_GROUND','AREF_COMMON','AREF_DIFF'])
	self.xInputTypeBox.SetSelection(2)
	sizer.Add(xInputTypeLabel,0,wx.ALIGN_RIGHT)
	sizer.Add(self.xInputTypeBox)

        yInputLabel = wx.StaticText(self, -1, "Y Input Channel: ")
	self.yInputBox = wx.Choice(self,-1, choices=['0','1','2','3','4','5','6','7'])
	self.yInputBox.SetSelection(2)
	sizer.Add(yInputLabel,0,wx.ALIGN_RIGHT)
	sizer.Add(self.yInputBox)

	yInputRangeLabel = wx.StaticText(self, -1, "Y Input Range: ")
	self.yInputRangeBox = wx.Choice(self,-1, choices=['0','1','2','3'])
	self.yInputRangeBox.SetSelection(1)
	sizer.Add(yInputRangeLabel,0,wx.ALIGN_RIGHT)
	sizer.Add(self.yInputRangeBox)

	yInputTypeLabel = wx.StaticText(self, -1, "Y Input Type: ")
	self.yInputTypeBox = wx.Choice(self,-1, choices=['AREF_GROUND','AREF_COMMON','AREF_DIFF'])
	self.yInputTypeBox.SetSelection(2)
	sizer.Add(yInputTypeLabel,0,wx.ALIGN_RIGHT)
	sizer.Add(self.yInputTypeBox)

        inputPeriodLabel = wx.StaticText(self, -1, "Samples per second: ")
        self.inputPeriodBox = wx.TextCtrl(self,-1,"3000")
        sizer.Add(inputPeriodLabel,0,wx.ALIGN_RIGHT)
        sizer.Add(self.inputPeriodBox)

        self.SetSizer(vertSizer)
        self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
        self.Fit()

    def PressOkButton(self,event):
	self.Close(1)

    def OnCloseWindow(self, event):
        self.MakeModal(False)
        self.Show(0)
	print self.getConfiguration()
        #self.Destroy()

    def getConfiguration(self):
	config = dict()
	config["device"] = self.deviceNameTextBox.GetValue()
	
        config["analog_period_ns"] = int(1e9/float(self.inputPeriodBox.GetValue()))

        x_channel = int(self.xInputBox.GetStringSelection())
        x_range = int(self.xInputRangeBox.GetStringSelection())
        x_aref = self.xInputTypeBox.GetStringSelection()

        y_channel = int(self.yInputBox.GetStringSelection())
        y_range = int(self.yInputRangeBox.GetStringSelection())
        y_aref = self.yInputTypeBox.GetStringSelection()

        config["analog"] = [[x_channel,x_range,x_aref],[y_channel, y_range, y_aref]]
	return config

app = wx.PySimpleApp()
frame = MainWindow()
app.MainLoop()
